////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file   CollabRCBot\OperationalTreeNode.h
///
/// @brief  Declares the CollabRC::Bot::AI::OperationalTreeNode class. 
////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once
#include "ComputationalTreeNode.h"
#include "OperationTreeNode.h"
#include <vector>

class TestOperationTreeNode;

namespace CollabRC
{
    namespace Bot
    {
        namespace AI
        {
            class ComputationalTreeNodeVisitor;

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            /// @class  OperationTreeNode
            ///
            /// @brief  Implementation of a ComputationalTreeNode that represents
            ///         an abstract operation.
            /// 
            /// Implementations of this abstract class must define the operation
            /// and register with this class statically. The registration allows
            /// for Mutate() operations to function by allowing an operation to 
            /// randomly transition to another type of operation.
            ///
            /// @author Matthew P. Del Buono
            /// @date   12/21/2009
            ////////////////////////////////////////////////////////////////////////////////////////////////////
            class OperationTreeNode : public ComputationalTreeNode
            {
            protected:
                class OperationTreeNodeFactory;
            private:
                static std::vector<const OperationTreeNodeFactory*> factories;
                static const OperationTreeNodeFactory* GetRandomFactory();
            protected:
                class OperationTreeNodeFactory
                {
                public:
                    virtual ComputationalTreeNode* Instantiate(const ComputationalTreeNode& lhs, const ComputationalTreeNode& rhs) const = 0;
                };

                /**
                 * @brief Registers an OperationTreeNodeFactory with the OperationTreeNode
                 * class.
                 *
                 * Registration permits a factory to be used in mutation operations. The factory
                 * will not be destroyed, so the resource must be tracked by its owner.
                 * 
                 * @version $Id$
                 * @author mpdelbuono
                 */
                inline static void RegisterOperationTreeNodeFactory(const OperationTreeNodeFactory* factory)
                {
                    factories.push_back(factory);
                }
            // TODO: Find a better way to let tests work...
#ifndef _INCLUDED_TEST_OPERATION_TREE_NODE_H
            private:
#endif
                ComputationalTreeNode *m_rhs;
                ComputationalTreeNode *m_lhs;

            public:
                OperationTreeNode(const ComputationalTreeNode& lhs, const ComputationalTreeNode& rhs);
                OperationTreeNode(const OperationTreeNode& copy);
                virtual ~OperationTreeNode(void);

                virtual float Compute() const;
                virtual unsigned int GetBranchDepth() const;
                virtual const ComputationalTreeNode& GetRandomBranch(unsigned int depth) const;
                virtual ComputationalTreeNode* Mutate(unsigned int depth) const;

                static ComputationalTreeNode* Cross(const ComputationalTreeNode& lhs, const ComputationalTreeNode &rhs);

                /**
                 * @brief Performs the mathematical operation that this OperationalTreeNode
                 * represents.
                 *
                 * If the operation is impossible due to a mathematical error
                 * (such as the square root of a negative number or division
                 * by zero), an IllegalOperationException must be thrown.
                 * 
                 * @param lhs the left hand side of the operation
                 * @param rhs the right hand side of the operation
                 * @return the result of the operation
                 */
                virtual float PerformOperation(float lhs, float rhs) const = 0;

                virtual void Accept(ComputationalTreeNodeVisitor& visitor);
            };

        }
    }
}

